#include "precompiled.h"
#include "networking.h"

ServerNetwork::ServerNetwork(void)
{
  WSAStartup(MAKEWORD(2,2), &wsadata);
}

ServerNetwork::~ServerNetwork(void)
{
  std::list<DWORD>::iterator iter;

  iter = joah_is_stupids.begin();

  while(iter != joah_is_stupids.end())
  {
    CloseHandle((HANDLE)(*iter));
    ++iter;
  }

  WSACleanup();
}

//will block for up to 20s if no server is broadcasting
void ServerNetwork::connect(void)
{
  int attempts = 0;//10 attempts, 1s between attempts
  char buffer[MAX_LEN];
  sockaddr_in them;
  int themsize = sizeof(them);

  recv_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

  //listen for a broadcast
  ////get data of server from broadcast
  while(attempts < 10)
  {
    err = recvfrom(recv_sock, buffer, MAX_LEN, 0, (sockaddr *)&them, &themsize);

    if(err == SOCKET_ERROR)
      throw "TACTICAL NUKE1";

    ++attempts;
    Sleep(1000);
  }

  clients.push_back(them);
  std::list<sockaddr_in>::iterator iter;
  iter = clients.end();
  attempts = 0;

  //message to send
  NetMes tos, tor;
  tos.validation = VALIDATE_ME;
  tos.type = CONREQ;
  ZeroMemory(&(tos.data[0]), MAX_LEN - sizeof(int) - sizeof(NetType));
  //do(up to 10s)
  //send message to server with a CONREQ
  ////while you dont receive a KEASHERE message
  do
  {
    err = sendto(send_sock, (char *)&tos, sizeof(tos), 0, (sockaddr *)&(*iter), sizeof(them));

    if(err == SOCKET_ERROR)
      throw "TACTICAL NUKE2";

    err = recvfrom(recv_sock, (char *)&tor, MAX_LEN, 0, (sockaddr *)&them, &themsize);

    if(err == SOCKET_ERROR)
      throw "TACTICAL NUKE3";

    //make sure the message we get is relevant
    if(err)
      if(((NetMes)tor).validation == VALIDATE_ME && ((NetMes)tor).type == KEASHERE)
        return;

    ++attempts;
    Sleep(1000);
  }while(attempts < 10);

  clients.pop_back();
}

void ServerNetwork::listen_udp(void)
{
  DBAG joah_is_stupid;

  //IF RANDOM CRASHES OCCUR LOOK HERE
  CreateThread(NULL, 0, listening_socket, (void*)this, 0, &joah_is_stupid);

  joah_is_stupids.push_back(joah_is_stupid);
}

DWORD WINAPI listening_socket(void* param)
{
  int stuff;
  ServerNetwork *net = (ServerNetwork *)param;
  char buffer[MAX_LEN];
  sockaddr_in them;
  int t = sizeof(them);

  while(1)
  {
    stuff = recvfrom(net->recv_sock, buffer, MAX_LEN - 1, 0, (sockaddr *)&them, &t);

    if(SOCKET_ERROR == stuff)
    {
      stuff = WSAGetLastError();
      if(WSAEWOULDBLOCK != stuff)
        return 0; //Failure
    }

    //these aren't the droids your looking for
    if(!(((NetMes *)buffer)->validation == VALIDATE_ME))
      continue;

    //they want to connect
    if(((NetMes *)buffer)->type == CONREQ)
    {
      NetMes hmmmmm;
      hmmmmm.validation = VALIDATE_ME;
      hmmmmm.type = KEASHERE;
      ZeroMemory(&(hmmmmm.data[0]), MAX_LEN - sizeof(int) - sizeof(NetType));

      net->clients.push_back(them);
      stuff = sendto(net->send_sock, (char *)&hmmmmm, MAX_LEN, 0, (sockaddr *)&them, sizeof(them));

      if(stuff == SOCKET_ERROR)
        throw "GEE THANKS KEA";
    }
  }

  return 0;
}

DWORD WINAPI broadcast_socket(void* param)
{
  int joah_is_stupid;//error
  ServerNetwork *net = (ServerNetwork *)param;//pointer to net obj that created this thread

  //message saying 'hey, this is my data, you can contact me'
  NetMes to_send;
  to_send.validation = VALIDATE_ME;
  to_send.type = BROADCASTINGSERVERDATAFORCLIENTSTOCONNECT;//lawl?
  ZeroMemory(&(to_send.data[0]), MAX_LEN - sizeof(int) - sizeof(NetType));

  //create broadcasting socket
  SOCKADDR_IN sbroadcast;
	sbroadcast.sin_family = AF_INET;
	sbroadcast.sin_port = htons(8001);
	sbroadcast.sin_addr.s_addr = INADDR_BROADCAST;

  SOCKET broadcast;

  broadcast = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

  joah_is_stupid = bind(broadcast, (const sockaddr*)&sbroadcast, sizeof(sbroadcast));

  while(1)
  {
    joah_is_stupid = sendto(broadcast, (char *)&to_send, MAX_LEN, 0, (sockaddr *)&sbroadcast, sizeof(sockaddr_in));
    Sleep(1000);
  }

  closesocket(broadcast);
}

void ServerNetwork::broadcast(void)
{
  DWORD joah_is_stupid;

  CreateThread(NULL, 0, broadcast_socket, (void*)this, 0, &joah_is_stupid);

  joah_is_stupids.push_back(joah_is_stupid);
}

void ServerNetwork::send_udp(char *_data, UINT len)
{
  std::list<sockaddr_in>::iterator iter;

  iter = clients.begin();

  while(iter != clients.end())
  {
    err = sendto(send_sock,_data,len, 0, (sockaddr *)&iter, sizeof(iter));

    //if client disconnected, remove from client list, now we dont send to clients that arent there
    if(err == SOCKET_ERROR)
      clients.erase(iter);

    ++iter;
  }
}

void ServerNetwork::disconnect(void)
{
  //will implement if we actually need to
}

std::list<NetMes> ServerNetwork::update(void)
{
  NetMes tempppppppppenis;
  ZeroMemory(&tempppppppppenis, sizeof(NetMes));
  sockaddr_in them;
  int themsize = sizeof(them);

  do
  {
    err = recvfrom(recv_sock, (char *)&tempppppppppenis, sizeof(tempppppppppenis), 0, (sockaddr *)&them, &themsize);

    if(err == SOCKET_ERROR)
      continue;

    if(err)
      messages_on_the_recv_sock_socket.push_back(tempppppppppenis);
  }while(err != SOCKET_ERROR && err);

  return messages_on_the_recv_sock_socket;
}
